package com.miyu.system.service.data.impl;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONReader;
import com.blinkfox.fenix.helper.StringHelper;
import com.blinkfox.fenix.specification.FenixSpecification;
import com.miyu.plugins.aop.CacheBatchEvict;
import com.miyu.plugins.exception.CosException;
import com.miyu.plugins.utils.Comutils;
import com.miyu.system.dao.data.*;
import com.miyu.system.pojo.system.*;
import com.miyu.system.service.data.SysDataService;
import com.miyu.system.utils.enumeration.R;
import com.miyu.system.utils.page.PageLimit;
import jakarta.persistence.EntityManager;
import lombok.RequiredArgsConstructor;
import net.dreamlu.mica.redis.cache.MicaRedisCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;

/**
 * 系统数据服务impl
 * @author miyu
 */
@Service
@RequiredArgsConstructor(onConstructor_ = {@Autowired})
public class SysDataServiceImpl implements SysDataService {
    /** 数据层 */
    private final SysDataDao dao;
    private final SysDictItemDao itemDao;
    private final SysDictDynaDao detailDao;
    private final SysTableDao tableDao;
    private final SysTableDetailDao tableDetailDao;
    @Value("${spring.use.database}")
    private  String dataBase;

    private final MicaRedisCache redisCache;

    private final EntityManager em;





    /**
     * 编辑系统字典
     * @param param 参数
     * @return {@link JSONObject}
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public JSONObject editSysDict(JSONObject param) {
        SysDict sysDict = JSONObject.parseObject(param.getString("sysDict"), SysDict.class);
        /*判断是否存在相同编码*/
        boolean existCode = isExistCode(sysDict);
        if(existCode){
            throw new CosException("编码已存在，请修改编码");
        }
        /*保存字典*/
        if(sysDict.getDictId()==null){
            sysDict.setCreateDate(LocalDateTimeUtil.now());
        }
        sysDict.setUpdateDate(LocalDateTimeUtil.now());
        dao.saveOrUpdateByNotNullProperties(sysDict);
        if(sysDict.getDataSource().equals("0")){
            /*判断是否静态字典*/
//           Integer id = sysDict.getDictId();
//
//            String sql = StringHelper.format("delete from {} where {} in :dictId", "SysDictItem", "dictId");
//            em.createQuery(sql)
//                    .setParameter("dictId", id)
//                    .executeUpdate();

            List<SysDictItem> sysDictItem = JSONArray.parseArray(param.getString("sysDictItem"), SysDictItem.class);
            sysDictItem.stream().forEach(item->item.setDictId(sysDict.getDictId()));
            itemDao.saveOrUpdateAllByNotNullProperties(sysDictItem);
        }else{
            SysDictDyna sysDictDyna = JSONObject.parseObject(param.getString("sysDictDyna"), SysDictDyna.class);
            sysDictDyna.setDictId(sysDict.getDictId());
            detailDao.saveOrUpdateByNotNullProperties(sysDictDyna);
        }
        //保存完后缓存字典数据
        return R.result("101");
    }

    private boolean isExistCode(SysDict sysDict) {
        boolean existCode = false;
        List<SysDict> list = dao.findAll(FenixSpecification.of(builder->builder.andEquals("dictCode",sysDict.getDictCode(),StrUtil.isNotBlank(sysDict.getDictCode())).build()));
        if(!list.isEmpty()) {
            if(sysDict.getDictId()!=null){
                if(!list.get(0).getDictId().equals(sysDict.getDictId())){
                    return true;
                }
            }else{
                return true;
            }
        }
        return existCode;
    }

    /**
     * 查询系统字典
     * @param param 参数
     * @return {@link JSONArray}
     */
    @Override
    public Page<SysDict> querySysDict(JSONObject param) {


        return dao.querySysDict(param);
    }

    /**
     * 查询系统字典项
     * @param param 参数
     * @return {@link JSONArray}
     */
    @Override
    public List<SysDictItem> querySysDictItem(SysDictItem param) {
        return itemDao.findAll(FenixSpecification.of(builder
                ->builder.andEquals("dictId",param.getDictId(),StrUtil.isNotBlank(String.valueOf(param.getDictId())))
                .build()), Sort.by("sortNo"));
    }

    /**
     * 查询系统字典细节动态
     * @param param 参数
     * @return {@link JSONObject}
     */
    @Override
    public SysDictDyna querySysDictDetail(SysDictDyna param) {
        return detailDao.findOne(FenixSpecification.of(builder ->
                builder.andEquals("dictId",param.getDictId(),StrUtil.isNotBlank(String.valueOf(param.getDictId()))).build())).orElse(null);
    }

    /**
     * ▽系统字典删除
     * 自定义批量删除
     * @param params 参数个数
     * @return {@link JSONObject}
     */
    @Override
//    @CacheEvict(cacheNames="system:sysDict")

    @CacheBatchEvict(cacheNames = "system:sysDictItem",type = "jsonArray",key = "#dictId")
    public JSONObject delSysDict(JSONArray params) {
        List<SysDict> sysRuleList = params.toJavaList(SysDict.class, JSONReader.Feature.IgnoreSetNullValue);
        List<Integer> dict = new ArrayList<Integer>();
        for (SysDict sysDict : sysRuleList) {
            Integer dictId = sysDict.getDictId();
            dict.add(dictId);
            if(sysDict.getDataSource().equals("0")){
                itemDao.delete(FenixSpecification.of(builder ->builder.andEquals("dictId",dictId).build()));
            }else{
                detailDao.delete(FenixSpecification.of(builder ->builder.andEquals("dictId",dictId).build()));
            }
        }
        dao.deleteAllInBatch(sysRuleList);
        return R.result("101");
    }
    /**
     * 手动缓存系统字典 清除所有字典缓存
     * @return {@link JSONObject}
     */
    @Override
    @CacheEvict(cacheNames = "system:sysDict", allEntries = true,beforeInvocation=true)
    public  List<SysDict>  cacheSysDict() {

        return dao.findAll(FenixSpecification.of(builder
                -> builder.andEquals("isStop","0").build()));
    }

    /**
     * 获取字典项通过代码
     * @param itemCode 代码
     * @return {@link JSONArray}
     */
    @Override
    @Cacheable(value  = "system:sysDict",key="#itemCode")
    public JSONArray getDictItemByCode(String itemCode) {
        SysDict sysDict = dao.findOne(FenixSpecification.of(builder
                -> builder.andEquals("isStop", "0").
                andEquals("dictCode", itemCode).build())).orElse(null);
        JSONArray result = new JSONArray();
        if (sysDict != null) {
            Integer dictId = sysDict.getDictId();
            switch (sysDict.getDataSource()) {
                case "0":
                    //静态字典
                    result = getStaticDictItemById(dictId);
                    break;
                case "1":
                    result = getTrendsDictItemByCode(dictId);
                    break;
                default:
                    break;
            }
        }else{
            throw new CosException("字典编码不存在!");
        }
        return result;
    }

    /**
     * 获取表名字
     * @return {@link JSONArray}
     */
    @Override
    public JSONArray getTableName() {
        return dao.getTableName(dataBase);
    }

    /**
     * 获取表列通过表
     * @param param 参数
     * @return {@link JSONArray}
     */
    @Override
    public JSONArray getTableColumnsByTable(JSONObject param) {
        return dao.getTableColumnsByTable(param,dataBase);
    }

    /**
     * 系统表编辑
     * @param param 参数
     * @return {@link JSONObject}
     */
    @Override
    public JSONObject sysTableEdit(JSONObject param) {
        SysTable sysTable = JSONObject.parseObject(param.getString("sysTable"),SysTable.class);
        List<SysTableDetail> details = JSONArray.parseArray(param.getString("sysTableDetail"),SysTableDetail.class);
        /*更新主表数据*/
        tableDao.save(sysTable);
        //删除明细
        tableDetailDao.deleteColumn(sysTable.getTableId());
        details.forEach(item->item.setTableId(sysTable.getTableId()));
        /*更新明细表数据*/
        tableDetailDao.saveAll(details);

        return R.result("101");
    }

    /**
     * 查询系统表
     * @param param 参数
     * @return {@link JSONArray}
     */
    @Override
    public Page<SysTable> querySysTable(JSONObject param) {
        String info = param.getString("info");
        Pageable  pageable= PageLimit.pageable(param);
        Page<SysTable> sysTables = tableDao.findAll(FenixSpecification.of(builder
                -> builder.andLike("tableName", info, StrUtil.isNotBlank(info))
                .orLike("showName", info, StrUtil.isNotBlank(info))
                .build()),pageable);

        return sysTables;
    }

    /**
     * ▽系统表
     * @param param 参数
     * @return {@link JSONArray}
     */
    @Override
    public JSONObject delSysTable(JSONArray param) {
        List<SysTable> list = JSONArray.parseArray(param.toString(),SysTable.class);
        list.forEach(item->{
            tableDetailDao.delete(FenixSpecification.of(builder->
                    builder.andLike("tableId", item.getTableId()).build()));
        });

        //批量删除主表
        tableDao.deleteAll(list);
        return R.result("101");
    }

    /**
     * 查询系统表细节
     * @param sysTable 系统表
     * @return {@link JSONArray}
     */
    @Override
    public JSONArray querySysTableDetail(SysTable sysTable) {
        StringBuffer sql = new StringBuffer();
        sql.append(" select detail_id,item_field, is_show,format,table_id ,item_type,title_show from sys_table_detail ");
        sql.append(" where table_id = '").append(sysTable.getTableId()).append("' order by order_num ");
        return dao.queryJSONArrayBySql(sql.toString());
    }

    /**
     * 查询系统表显示
     * @param param 参数
     * @return {@link JSONObject}
     */
    @Override
    public JSONObject querySysTableShow(JSONObject param) {
        SysTable sysTable = JSONObject.parseObject(param.toString(),SysTable.class);
        //表头数据 +表格数据一次返回
        JSONObject result = new JSONObject();

        JSONArray details = querySysTableDetail(sysTable);
        JSONArray headers = new JSONArray();
        StringBuffer sql = new StringBuffer();
        sql.append(" select row_number() over () num");
        for(int i=0;i<details.size();i++) {
            JSONObject item = details.getJSONObject(i);

            String itemField = item.getString("itemField");
            String titleShow = item.getString("titleShow");
            String isShow = item.getString("isShow");
            String format = item.getString("format");
            String itemType = item.getString("itemType");
            sql.append(",").append(itemField);
            if(!isShow.equals("1")) {
                continue;
            }
            JSONObject header = new JSONObject();
            if(StrUtil.isNotBlank(titleShow)){
                header.put("title",titleShow);
            }else{
                header.put("title",itemField);
            }
            header.put("tooltip",true);
            header.put("key", Comutils.caseHump(itemField));
            //数字居左其其他居右
            if(itemType.contains("int") || itemType.contains("num")){
                header.put("align","right");
            }else{
                header.put("align","left");
            }
            header.put("width","150");
            headers.add(header);
        }
        sql.substring(0,sql.length()-2);
        // sql取值
        sql.append("  from ").append(sysTable.getTableName());
        JSONArray data = dao.queryJSONArrayBySql(sql.toString());
        result.put("header",headers);
        result.put("data",data);
        return result;
    }

    /**
     * 缓存字典项通过代码
     * 每次都更新
     * 与取值不同 取值不需要清除缓存 直接取值
     * 非接口调用不回缓存
     * @param dictCode 字典代码
     * @return {@link JSONArray}
     */
    @Override
    @CachePut(cacheNames  = "system:sysDict",key="#dictCode")
    public JSONArray cacheDictItemByCode(String dictCode) {
        return getDictItemByCode(dictCode);
    }

    /**
     * 查询rediskeys
     * 字典 key规律  1模块 2 类型 3 key
     * 数据
     * @param param
     * @return
     */
    @Override
    public JSONArray queryRedisKeys(JSONObject param) {
        JSONArray result = new JSONArray();
        JSONObject item = new JSONObject();
        int dataBase = 0;
        if(param.containsKey("dbIndex")){
            dataBase = param.getInteger("dbIndex");
        }
        String modelType="system";
        if(param.containsKey("model")){
            modelType = param.getString("model");
        }

        Set<String> keys =  redisCache.keys(modelType.concat("*"));
          for (String key : keys) {
            String[] value = key.split(":");
            String model = value[0];
            String type = value[1];
            if(!item.containsKey(model+type)) {
                JSONObject json = new JSONObject();
                json.put("model", RedisType.getInfoByCode(model));
                json.put("modelCode", model);
                json.put("keyType", type);
                result.add(json);
                item.put(model+type, value);
            }
        }
        return result;
    }

    /**
     * 查询rediskeysbytype
     *
     * @param param
     * @return
     */
    @Override
    public JSONArray queryRedisKeysByType(JSONObject param) {
        int dataBase = 0;
        if(param.containsKey("dbIndex")){
            dataBase = param.getInteger("dbIndex");
        }
        JSONArray result = new JSONArray();
        Set<String> keys =  redisCache.keys(param.getString("key").concat("*"));
        for (String key : keys) {
            JSONObject item = new JSONObject();
           String str =  key.substring(param.getString("key").length()+1, key.length());
           String [] items = str.split(":");
           if(items.length>1){
               //含有子集
           }else{
               item.put("label", str);
           }

           result.add(item);

        }
        return result;
    }

    /**
     * 获取趋势字典项通过代码
     * @param dictId 字典id
     * @return {@link JSONArray}
     */
    private JSONArray getTrendsDictItemByCode(Integer dictId) {
        JSONArray result = new JSONArray();
        StringBuffer sql = new StringBuffer()  ;
        SysDict sysDict = dao.findOne(FenixSpecification.of(builder  -> builder.andEquals("dictId", dictId).build())).orElse(null);
        SysDictDyna sysDictDyna = detailDao.findOne(FenixSpecification.of(builder -> builder.andEquals("dictId", dictId).build())).orElse(null);
        if(sysDictDyna!=null){
            String sqlField =sysDictDyna.getSqlField();
            if(sysDict!=null &&sysDict.getIsTree().equals("0")){
                sql.setLength(0);
                sql.append(sqlField);
                result = dao.queryJSONArrayBySql(sql.toString());
            }else{
                sql.setLength(0);
                sql.append(sqlField);
                result = dao.queryJSONArrayBySql(sql.toString());
                JSONArray dictTree = new JSONArray();
                result = getDictTree(result,dictTree);
            }
        }

        return result;
    }

    /**
     * 获取字典树
     * @param result   结果
     * @param dictTree 字典树
     * @return {@link JSONArray}
     */
    private JSONArray getDictTree(JSONArray result, JSONArray dictTree) {
        for (int i = 0; i < result.size(); i++) {
            JSONObject menu = result.getJSONObject(i);
            if(menu.getString("parentId").equals("-1")){
                JSONArray rs = getChildList(menu.getString("childId"),result);
                if(!rs.isEmpty()){
                    menu.put("children",rs);
                }
                dictTree.add(menu);
            }
        }
        return dictTree;
    }

    /**
     * 获取孩子列表
     * @param menuId 菜单id
     * @param result 结果
     * @return {@link JSONArray}
     */
    private JSONArray getChildList(String menuId, JSONArray result) {
        JSONArray rs = new JSONArray();
        for (int i = 0; i < result.size(); i++) {
            JSONObject menu = result.getJSONObject(i);
            if (menu.getString("parentId").equals(menuId)) {
                JSONArray menuList = getChildList(menu.getString("childId"),result);
                if (!menuList.isEmpty()) {
                    menu.put("children",menuList);
                }
                rs.add(menu);
                // rs.sort(Comparator.comparing(obj -> ((JSONObject) obj).getInteger("orderNum")));
            }
        }
        return rs;
    }

    /**
     * 获取静态字典项通过代码
     *
     * @param dictId 项目代码
     * @return {@link JSONArray}
     */
    private JSONArray getStaticDictItemById(Integer dictId) {
        List<SysDictItem> list = itemDao.findAll(FenixSpecification.of(builder -> builder.andEquals("dictId", dictId).build()), Sort.by("sortNo"));
        return  JSONArray.parseArray(JSONObject.toJSONString(list));
    }

    /**
     * dict 格式化处理
     * @param sysDictList 系统字典列表
     * @return {@link JSONObject}
     */
    private JSONObject dictFormat(JSONArray sysDictList) {
        JSONObject dict = new JSONObject();
        String key = "dictCode";
        if (!sysDictList.isEmpty()) {
            for (int i = 0; i < sysDictList.size(); i++) {
                JSONObject obj = sysDictList.getJSONObject(i);
                String itemCode = obj.getString("itemCode");
                if (StrUtil.isNotBlank(itemCode)) {
                    /** 创建临时json*/
                    JSONObject dt = new JSONObject();
                    dt.put("value", obj.getString("itemCode"));
                    dt.put("label", obj.getString("itemName"));
                    dt.put("orderNum", obj.getString("orderNum"));
                    String isStop = obj.getString("isStop");
                    if ("1".equals(isStop)) {
                        dt.put("disabled", true);
                    } else {
                        dt.put("disabled", false);
                    }
                    /** 已存在*/
                    if (dict.containsKey(obj.getString(key))) {
                        JSONArray jsonArray = dict.getJSONArray(obj.getString(key));
                        jsonArray.add(dt);
                        jsonArray.sort(Comparator.comparing(st -> ((JSONObject) st).getString("orderNum")));
                        dict.put(obj.getString("dictCode"), jsonArray);
                    } else {
                        /** 新增时*/
                        JSONArray ja = new JSONArray();
                        ja.add(dt);
                        dict.put(obj.getString("dictCode"), ja);
                    }
                }

            }
        }
        return dict;
    }

}
